defmodule FtpWorker do
	@moduledoc false

	use GenServer
	require Logger
	import ErrCode, only: [unexpected_error: 0, please_wait: 0]

	@pool_name :ftp_pool
	@file_size_10m 1024 * 1024 * 10

	def list_dir(dir \\ "/", timeout \\ 5000) do
		call({:list_dir, dir}, timeout)
	end

	def download_file(filename, local_dir, timeout \\ 5000) do
		file_info = file_info(filename)
		file_info.size > @file_size_10m && throw({:error, "文件大于10M"})
		call({:download_file, filename, local_dir}, timeout)
	end


	def upload_file(filename, dir, timeout \\ 5000) do
		call({:upload_file, filename, dir}, timeout)
	end

	def file_info(filename, timeout \\ 5000) do
		call({:file_info, filename}, timeout)
	end

	def make_dir(dir, timeout \\ 5000) do
		call({:make_dir, dir}, timeout)
	end
	def delete_dir(dir, timeout \\ 5000) do
		call({:delete_dir, dir}, timeout)
	end

	defp call(msg, timeout) do
		worker = :poolboy.checkout(@pool_name, false, 5000)
		worker == :full && throw({:error, please_wait()})
		case GenServer.call(worker, msg, timeout) do
			{:ok, resp} ->
				resp
			:ok ->
				:ok
			{:error, err} ->
				throw({:error, err, "请检查ftp服务是否启动"})
			err ->
				throw({:error, err})
		end
	end


	def children(_module) do
		args = AppConfigure.fetch(:ftp)
		{size, args} = Keyword.pop(args, :pool_size)
		{max_overflow, args} = Keyword.pop(args, :max_overflow)
		pool_options = [
			name: {:local, @pool_name},
			worker_module: __MODULE__,
			size: size,
			max_overflow: max_overflow
		]
		[
			:poolboy.child_spec(@pool_name, pool_options, [args])
		]
	end

	def start_link(_) do
		GenServer.start_link(__MODULE__, [])
	end

	def init(_) do
		Process.send_after(self(), :connect, 1000)
		{:ok, nil}
	end


	def handle_call(msg, _from, state) do
		{reply, state} = do_call(msg, state)
		{:reply, reply, state}
	end

	def handle_cast(_msg, state) do
		{:noreply, state}
	end

	def handle_info(:connect, state) do
		{_, state} = connect(state)
		{:noreply, state}
	end
	def handle_info({:DOWN, _ref, :process, pid, reason}, state) do
		Logger.error("Pid #{inspect(pid)} Died, Reason: #{reason}")
		{:noreply, state}
	end
	def handle_info({:EXIT, pid, reason}, state) do
		Logger.error("Pid #{inspect(pid)} Died, Reason: #{reason}")
		{:noreply, state}
	end
	def handle_info(msg, state) do
		Logger.error(inspect(msg))
		{:noreply, state}
	end

	def terminate(reason, state) do
		Logger.error("Reason: #{reason}, state: #{state}")
		:ok
	end

	def get_pids() do
		:sys.get_state(Process.whereis(@pool_name))
		|> elem(2)
		|> Enum.map(&GenServer.call(&1, :state).channel_pid)
	end

	defp do_call(msg, nil) do
		case connect(nil) do
			{:ok, state} ->
				do_call(msg, state)
			{:error, nil} ->
				{"请检查ftp服务器是否启动", nil}
		end
	end
	defp do_call({:download_file, filename, local_dir}, state) do
		%{remote_path: remote_path, local_path: local_path} = state
		file = local_path <> local_dir <> "/" <> filename
		remote_file = remote_path <> filename
		send_request(state, :download_file, [remote_file, file])
	end
	defp do_call({:upload_file, filename, dir}, state) do
		%{remote_path: remote_path} = state
		remote_file = remote_path <> dir <> "/" <> :filename.basename(filename)
		send_request(state, :upload_file, [filename, remote_file])
	end
	defp do_call({:make_dir, dir}, state) do
		%{remote_path: remote_path} = state
		dir = remote_path <> dir
		send_request(state, :make_dir, [dir])
	end
	defp do_call({:delete_dir, dir}, state) do
		%{remote_path: remote_path} = state
		dir = remote_path <> dir
		send_request(state, :delete_dir, [dir])
	end
	defp do_call({:list_dir, dir}, state) do
		%{remote_path: remote_path} = state
		send_request(state, :list_dir, [remote_path <> dir])
	end
	defp do_call({:file_info, filename}, state) do
		%{remote_path: remote_path} = state
		send_request(state, :file_info, [remote_path <> filename])
	end
	defp do_call(:state, state) do
		{state.ftp_conn, state}
	end
	defp do_call(msg, state) do
		Logger.error(inspect(msg))
		{unexpected_error(), state}
	end


	defp send_request(state, func, args) do
		{apply(SFTPClient, func, [Map.get(state, :ftp_conn) | args]), state}
	end

	def connect(nil) do
		[
			host: host,
			port: port,
			user: user,
			password: passwd,
			local_path: local_path,
			remote_path: remote_path,
		] = AppConfigure.fetch(:ftp)
		    |> Keyword.drop([:pool_size, :max_overflow])
		config = %SFTPClient.Config{
			host: host,
			port: port,
			user: user,
			password: passwd
		}
		case SFTPClient.connect(config) do
			{:ok, conn} ->
				Logger.info("Ftp Connect Ok")
				{:ok, %{ftp_conn: conn, remote_path: remote_path, local_path: local_path}}
			{:error, err} ->
				Logger.error(inspect({"Ftp Connect Error, ", err}))
				{:error, nil}
		end
	end
	def connect(state) do
		%{ftp_conn: conn} = state
		if conn == nil || !Process.alive?(conn.channel_pid) do
			connect(nil)
		else
			{:ok, state}
		end
	end


end